#![no_std]
#![no_main]

extern crate alloc;

use alloc::{sync::Arc, vec};
use core::{
    sync::atomic::{AtomicIsize, Ordering},
    time::Duration,
};

use seminix::{
    bsp::{
        BspDefine,
        irqchip::IrqType,
        memrange::MemDefine,
        smp::{PsciCompatible, PsciEnableMethod, SmpDefine},
    },
    bsp_data::define_bsp_data,
    drivers::{
        clocksource::arm_arch_timer::{ArmClock, ArmClockType},
        console::amba_pl011::Pl011,
        irqchip::irq_gic::IrqchipGic,
    },
    println,
    processor::{cpu_relax, this_processor_id},
    sched::task::{current, current_fast},
    space::{
        addr::{Paddr, Uaddr, mem_dump},
        kalloc::kalloc_dump,
        mm::{MmapPage, VmFlags, mmap, munmap},
    },
    time::{
        hrtimer::{Hrtimer, HrtimerMode, HrtimerRestart, hrtimer_start},
        timekeeping::{ktime_get, ktime_get_seconds},
    },
    utils::sizes::SZ_4G,
};
struct QemuVirtBsp;

#[define_bsp_data(Pl011, IrqchipGic, ArmClock)]
static QEMUVIRT_BSPDATA: QemuVirtBsp = QemuVirtBsp;

impl BspDefine<Pl011, IrqchipGic, ArmClock> for QemuVirtBsp {
    const DEFINE_SMP: SmpDefine = SmpDefine::create(
        PsciCompatible::Psci1_0,
        PsciEnableMethod::Hvc,
        0xc4000003,
        [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
        8,
    );

    const DEFINE_MEMS: MemDefine =
        MemDefine::create([(0x40000000, SZ_4G), (0, 0), (0, 0), (0, 0)], 1);

    const DEFINE_CONSOLE: Pl011 = Pl011::create(Paddr::from(0x9000000), 100, 115200);

    const DEFINE_IRQCHIP: IrqchipGic =
        IrqchipGic::create(Paddr::from(0x8000000), 0x10000, Paddr::from(0x8010000), 0x10000);

    // phys hwirq: 30
    // virt hwirq: 27
    const DEFINE_CLOCK: ArmClock =
        ArmClock::create(ArmClockType::Virt, IrqType::IRQ_TYPE_LEVEL_HIGH, 27);

    fn late_init(&self) {
        mem_dump();
        kalloc_dump();

        let mut v = vec![2, 5];
        v.push(4);
        v.push(6);
        println!("{:?}, cap {}", v, v.capacity());
        println!(
            "current task name {}, cpu {}, pid {}",
            current().name(),
            current_fast().cpu(),
            current().pid()
        );

        timer_test();

        mm_test();
        loop {
            cpu_relax();
        }
    }

    fn secondary_init(&self) {
        let mut i = 40000000;
        while i != 0 {
            cpu_relax();
            i -= 1;
        }
        println!(
            "current task name {}, cpu {}, pid {}",
            current().name(),
            current_fast().cpu(),
            current().pid()
        );
        println!("current ns {}", ktime_get());
        timer_test();
    }
}

static TIMER_COUNT: AtomicIsize = AtomicIsize::new(3);

fn tes(timer: &Hrtimer) -> HrtimerRestart {
    let cpu = this_processor_id();
    if cpu == 0 && TIMER_COUNT.fetch_sub(1, Ordering::Relaxed) > 0 {
        println!("timer: current cpu {} seconds {}", this_processor_id(), ktime_get_seconds());
    }
    let t = match cpu {
        0 => Duration::from_secs(1),
        1 => Duration::from_secs(2),
        2 => Duration::from_secs(3),
        3 => Duration::from_secs(4),
        4 => Duration::from_secs(5),
        5 => Duration::from_secs(6),
        6 => Duration::from_secs(7),
        7 => Duration::from_secs(8),
        _ => unreachable!(),
    };
    timer.forward_now(t.as_nanos() as u64);
    HrtimerRestart::Restart
}

fn timer_test() {
    let timer = Hrtimer::create(HrtimerMode::Rel, tes, None);
    let t = Duration::from_secs(1);
    hrtimer_start(Arc::new(timer), t.as_nanos() as u64);
}

fn mm_test() {
    let uaddr0 = Uaddr::from(0x500000);
    let vm_flags = VmFlags::VM_READ | VmFlags::VM_WRITE;
    let page_size = vm_flags.page_size();
    let count = 5;
    let mmap_page = Arc::new(MmapPage::create(vm_flags, count));
    mmap(current().mm().unwrap(), uaddr0, page_size * count, vm_flags, mmap_page).unwrap();

    mm_write(uaddr0, 3);
    assert_eq!(mm_read(uaddr0), 3);

    mm_write(uaddr0, 30);
    assert_eq!(mm_read(uaddr0), 30);

    let uaddr1 = Uaddr::from(0x450000000);
    let vm_flags = VmFlags::VM_READ | VmFlags::VM_WRITE | VmFlags::VM_IO | VmFlags::VM_HUGE;
    let page_size = vm_flags.page_size();
    let count = 5;
    let mmap_page = Arc::new(MmapPage::create(vm_flags, count));
    mmap(current().mm().unwrap(), uaddr1, page_size * count, vm_flags, mmap_page).unwrap();

    mm_write(uaddr1, 38);
    assert_eq!(mm_read(uaddr1), 38);

    mm_write(uaddr1, 308);
    assert_eq!(mm_read(uaddr1), 308);

    munmap(current().mm().unwrap(), uaddr0).unwrap();
    munmap(current().mm().unwrap(), uaddr1).unwrap();

    // 共享地址空间测试
    let uaddr0 = Uaddr::from(0x500000);
    let uaddr1 = Uaddr::from(0x15000000);
    let vm_flags = VmFlags::VM_READ | VmFlags::VM_WRITE;
    let page_size = vm_flags.page_size();
    let count = 5;
    let mmap_page = Arc::new(MmapPage::create(vm_flags, count));
    mmap(current().mm().unwrap(), uaddr0, page_size * count, vm_flags, mmap_page.clone()).unwrap();
    mmap(current().mm().unwrap(), uaddr1, page_size * count, vm_flags, mmap_page).unwrap();

    mm_write(uaddr0, 38);
    assert_eq!(mm_read(uaddr1), 38);
    mm_write(uaddr1, 0x5555);
    assert_eq!(mm_read(uaddr0), 0x5555);

    munmap(current().mm().unwrap(), uaddr0).unwrap();
    munmap(current().mm().unwrap(), uaddr1).unwrap();
}

fn mm_write(uaddr: Uaddr, val: usize) {
    let raw = uaddr.to_value() as *mut usize;
    unsafe {
        *raw = val;
    }
}

fn mm_read(uaddr: Uaddr) -> usize {
    let raw = uaddr.to_value() as *mut usize;
    unsafe { *raw }
}
